#include "DIVA_InputManager.h"
#include <cassert>

// 用來依據執行的DIVA模式而skip部分程式碼
#include "DIVA_Application.h"

DIVA_InputManager *DIVA_InputManager::mInputManager;

DIVA_InputManager::DIVA_InputManager( void ) :
    mMouse( 0 ),
    mKeyboard( 0 ),
    mInputSystem( 0 ) {
}

DIVA_InputManager::~DIVA_InputManager( void ) {
    if( mInputSystem ) {
        if( mMouse ) {
            mInputSystem->destroyInputObject( mMouse );
            mMouse = 0;
        }

        if( mKeyboard ) {
            mInputSystem->destroyInputObject( mKeyboard );
            mKeyboard = 0;
        }

        if( mJoysticks.size() > 0 ) {
            itJoystick    = mJoysticks.begin();
            itJoystickEnd = mJoysticks.end();
            for(; itJoystick != itJoystickEnd; ++itJoystick ) {
                mInputSystem->destroyInputObject( *itJoystick );
            }

            mJoysticks.clear();
        }

        // If you use OIS1.0RC1 or above, uncomment this line
        // and comment the line below it
		mInputSystem->destroyInputSystem( mInputSystem );
        //mInputSystem->destroyInputSystem();
		
		
        mInputSystem = 0;

        // Clear Listeners
        mKeyListeners.clear();
        mMouseListeners.clear();
        mJoystickListeners.clear();
    }
}

void DIVA_InputManager::initialise( Ogre::RenderWindow *renderWindow ) {
  
	// Commented out by Luke since it will cause run time OIS exception.
	
	 // if( !mInputSystem ) {
  //      // Setup basic variables
  //      OIS::ParamList paramList;    
  //      size_t windowHnd = 0;
  //      std::ostringstream windowHndStr;

  //      // Get window handle
  //      renderWindow->getCustomAttribute( "WINDOW", &windowHnd );

  //      // Fill parameter list
  //      windowHndStr << (unsigned int) windowHnd;
  //      paramList.insert( std::make_pair( std::string( "WINDOW" ), windowHndStr.str() ) );

		////(useful for non fullscreen mode, especially when debugging) 
		////if you want to see the mouse cursor and be able to move it outside the window, use the keyboard outside the running app, 
		////you might want to try the following : 
		//// insert the following right before calling mInputSystem = OIS::InputManager::createInputSystem( paramList );
		////generally speaking, adding these lines you can hava an default cursor
		//#if defined OIS_WIN32_PLATFORM
		//paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
		//paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
		//paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
		//paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
		//#elif defined OIS_LINUX_PLATFORM
		//paramList.insert(std::make_pair(std::string("x11_mouse_grab"), std::string("false")));
		//paramList.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("false")));
		//paramList.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
		//paramList.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
		//#endif


  //      // Create inputsystem
  //      mInputSystem = OIS::InputManager::createInputSystem( paramList );

  //      // If possible create a buffered keyboard
		//if( mInputSystem->numKeyboards() > 0 ) {
  //          mKeyboard = static_cast<OIS::Keyboard*>( mInputSystem->createInputObject( OIS::OISKeyboard, true ) );
  //          mKeyboard->setEventCallback( this );
  //      }

  //      // If possible create a buffered mouse
  //      if( mInputSystem->numMice() > 0 ) {
  //          mMouse = static_cast<OIS::Mouse*>( mInputSystem->createInputObject( OIS::OISMouse, true ) );
  //          mMouse->setEventCallback( this );

  //          // Get window size
  //          unsigned int width, height, depth;
  //          int left, top;
  //          renderWindow->getMetrics( width, height, depth, left, top );

  //          // Set mouse region
  //          this->setWindowExtents( width, height );
  //      }

  //      // If possible create all joysticks in buffered mode
		//if( mInputSystem->numJoySticks() > 0 ) {
  //          mJoysticks.resize( mInputSystem->numJoySticks() );

  //          itJoystick    = mJoysticks.begin();
  //          itJoystickEnd = mJoysticks.end();
  //          for(; itJoystick != itJoystickEnd; ++itJoystick ) {
  //              (*itJoystick) = static_cast<OIS::JoyStick*>( mInputSystem->createInputObject( OIS::OISJoyStick, true ) );
  //              (*itJoystick)->setEventCallback( this );
  //          }
  //      }
  //  }
}

void DIVA_InputManager::capture( void ) {
    // Need to capture / update each device every frame
    if( mMouse ) {
        mMouse->capture();
    }

    if( mKeyboard ) {
		mKeyboard->capture();
	}

    if( mJoysticks.size() > 0 ) {
        itJoystick    = mJoysticks.begin();
        itJoystickEnd = mJoysticks.end();
        for(; itJoystick != itJoystickEnd; ++itJoystick ) {
            (*itJoystick)->capture();
        }
    }

}

void DIVA_InputManager::addKeyListener( OIS::KeyListener *keyListener, const std::string& instanceName ) {
    if( mKeyboard ) {
        // Check for duplicate items
        itKeyListener = mKeyListeners.find( instanceName );
        if( itKeyListener == mKeyListeners.end() ) {
            mKeyListeners[ instanceName ] = keyListener;
			// added by YPC to make a listener be enabled or disabled.
			enKeyListeners[ instanceName ] = true ;
        }
        else {
            // Duplicate Item
        }
    }
}

void DIVA_InputManager::addMouseListener( OIS::MouseListener *mouseListener, const std::string& instanceName ) {
    if( mMouse ) {
        // Check for duplicate items
        itMouseListener = mMouseListeners.find( instanceName );
        if( itMouseListener == mMouseListeners.end() ) {
            mMouseListeners[ instanceName ] = mouseListener;
			// added by YPC to make a listener be enabled or disabled.
			enMouseListeners[ instanceName ] = true ;
        }
        else {
            // Duplicate Item
        }
    }
}

void DIVA_InputManager::addJoystickListener( OIS::JoyStickListener *joystickListener, const std::string& instanceName ) {
    if( mJoysticks.size() > 0 ) {
        // Check for duplicate items
        itJoystickListener = mJoystickListeners.find( instanceName );
        if( itJoystickListener == mJoystickListeners.end() ) {
            mJoystickListeners[ instanceName ] = joystickListener;
			// added by YPC to make a listener be enabled or disabled.
			enJoystickListeners[ instanceName ] = true ;
        }
        else {
            // Duplicate Item
        }
    }
}

void DIVA_InputManager::removeKeyListener( const std::string& instanceName ) {
    // Check if item exists
    itKeyListener = mKeyListeners.find( instanceName );
    if( itKeyListener != mKeyListeners.end() ) {
        mKeyListeners.erase( itKeyListener );
		enKeyListeners.erase( instanceName);
    }
    else {
        // Doesn't Exist
    }
}

void DIVA_InputManager::removeMouseListener( const std::string& instanceName ) {
    // Check if item exists
    itMouseListener = mMouseListeners.find( instanceName );
    if( itMouseListener != mMouseListeners.end() ) {
        mMouseListeners.erase( itMouseListener );
		enMouseListeners.erase( instanceName);
    }
    else {
        // Doesn't Exist
    }
}

void DIVA_InputManager::removeJoystickListener( const std::string& instanceName ) {
    // Check if item exists
    itJoystickListener = mJoystickListeners.find( instanceName );
    if( itJoystickListener != mJoystickListeners.end() ) {
        mJoystickListeners.erase( itJoystickListener );
		enJoystickListeners.erase( instanceName);
    }
    else {
        // Doesn't Exist
    }
}

void DIVA_InputManager::removeKeyListener( OIS::KeyListener *keyListener ) {
    itKeyListener    = mKeyListeners.begin();
    itKeyListenerEnd = mKeyListeners.end();
    for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
        if( itKeyListener->second == keyListener ) {
            mKeyListeners.erase( itKeyListener );
            break;
        }
    }
}

void DIVA_InputManager::removeMouseListener( OIS::MouseListener *mouseListener ) {
    itMouseListener    = mMouseListeners.begin();
    itMouseListenerEnd = mMouseListeners.end();
    for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
        if( itMouseListener->second == mouseListener ) {
            mMouseListeners.erase( itMouseListener );
            break;
        }
    }
}

void DIVA_InputManager::removeJoystickListener( OIS::JoyStickListener *joystickListener ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        if( itJoystickListener->second == joystickListener ) {
            mJoystickListeners.erase( itJoystickListener );
            break;
        }
    }
}

void DIVA_InputManager::removeAllListeners( void ) {
    mKeyListeners.clear();
    mMouseListeners.clear();
    mJoystickListeners.clear();
	
}

void DIVA_InputManager::removeAllKeyListeners( void ) {
    mKeyListeners.clear();
}

void DIVA_InputManager::removeAllMouseListeners( void ) {
    mMouseListeners.clear();
}

void DIVA_InputManager::removeAllJoystickListeners( void ) {
    mJoystickListeners.clear();
}

void DIVA_InputManager::setWindowExtents( int width, int height ) {
    // Set mouse region (if window resizes, we should alter this to reflect as well)
    const OIS::MouseState &mouseState = mMouse->getMouseState();
    mouseState.width  = width;
    mouseState.height = height;
}

OIS::Mouse* DIVA_InputManager::getMouse( void ) {
    return mMouse;
}

OIS::Keyboard* DIVA_InputManager::getKeyboard( void ) {
    return mKeyboard;
}

OIS::JoyStick* DIVA_InputManager::getJoystick( unsigned int index ) {
    // Make sure it's a valid index
    if( index < mJoysticks.size() ) {
        return mJoysticks[ index ];
    }

    return 0;
}

int DIVA_InputManager::getNumOfJoysticks( void ) {
    // Cast to keep compiler happy ^^
    return (int) mJoysticks.size();
}

bool DIVA_InputManager::keyPressed( const OIS::KeyEvent &e ) {
    itKeyListener    = mKeyListeners.begin();
    itKeyListenerEnd = mKeyListeners.end();
    for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
		// get the boolean value to see if this listener is disabled
		std::string instanceName = 	itKeyListener->first;
		std::map<std::string, bool>::iterator itr = enKeyListeners.find(instanceName) ;
		assert(itr != enKeyListeners.end());
		if (itr->second) { // if this listener is not disalbed, call the listener 
			itKeyListener->second->keyPressed( e );
			/// 把目前的事件加入到持續性事件 map 中用以讓key press 的事件持續進行
			keyEventCont.insert( std::make_pair(itKeyListener->second , e) );
		}
    }

    return true;
}

bool DIVA_InputManager::keyReleased( const OIS::KeyEvent &e ) {
    itKeyListener    = mKeyListeners.begin();
    itKeyListenerEnd = mKeyListeners.end();
    for(; itKeyListener != itKeyListenerEnd; ++itKeyListener ) {
	
		
		std::string instanceName = 	itKeyListener->first;
		std::map<std::string, bool>::iterator itr = enKeyListeners.find(instanceName) ;
		assert(itr != enKeyListeners.end());
		if (itr->second) {// if this listener is not disalbed, call the listener 
			/// 移除當前keypress的event
			keyEventCont.erase(itKeyListener->second);
			itKeyListener->second->keyReleased( e );
		}
	}

    return true;
}

bool DIVA_InputManager::mouseMoved( const OIS::MouseEvent &e ) {
    itMouseListener    = mMouseListeners.begin();
    itMouseListenerEnd = mMouseListeners.end();
    for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		// get the boolean value to see if this listener is disabled
		std::string instanceName = 	itMouseListener->first;
		std::map<std::string, bool>::iterator itr = enMouseListeners.find(instanceName) ;
		assert(itr != enMouseListeners.end());
		if (itr->second) // if this listener is not disalbed, call the listener 
			itMouseListener->second->mouseMoved( e );
    }

    return true;
}

bool DIVA_InputManager::mousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
    itMouseListener    = mMouseListeners.begin();
    itMouseListenerEnd = mMouseListeners.end();
    for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		// get the boolean value to see if this listener is disabled
		std::string instanceName = 	itMouseListener->first;
		std::map<std::string, bool>::iterator itr = enMouseListeners.find(instanceName) ;

		assert(itr != enMouseListeners.end());
		if (itr->second) // if this listener is not disalbed, call the listener 
			itMouseListener->second->mousePressed( e, id );
    }

    return true;
}

bool DIVA_InputManager::mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id ) {
    itMouseListener    = mMouseListeners.begin();
    itMouseListenerEnd = mMouseListeners.end();
    for(; itMouseListener != itMouseListenerEnd; ++itMouseListener ) {
		// get the boolean value to see if this listener is disabled
		std::string instanceName = 	itMouseListener->first;
		std::map<std::string, bool>::iterator itr = enMouseListeners.find(instanceName) ;

		assert(itr != enMouseListeners.end());
		if (itr->second) // if this listener is not disalbed, call the listener 
			itMouseListener->second->mouseReleased( e, id );
    }

    return true;
}

bool DIVA_InputManager::povMoved( const OIS::JoyStickEvent &e, int pov ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        itJoystickListener->second->povMoved( e, pov );
    }

    return true;
}

bool DIVA_InputManager::axisMoved( const OIS::JoyStickEvent &e, int axis ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        itJoystickListener->second->axisMoved( e, axis );
    }

    return true;
}

bool DIVA_InputManager::sliderMoved( const OIS::JoyStickEvent &e, int sliderID ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        itJoystickListener->second->sliderMoved( e, sliderID );
    }

    return true;
}

bool DIVA_InputManager::buttonPressed( const OIS::JoyStickEvent &e, int button ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        itJoystickListener->second->buttonPressed( e, button );
    }

    return true;
}

bool DIVA_InputManager::buttonReleased( const OIS::JoyStickEvent &e, int button ) {
    itJoystickListener    = mJoystickListeners.begin();
    itJoystickListenerEnd = mJoystickListeners.end();
    for(; itJoystickListener != itJoystickListenerEnd; ++itJoystickListener ) {
        itJoystickListener->second->buttonReleased( e, button );
    }

    return true;
}

DIVA_InputManager* DIVA_InputManager::getSingletonPtr( void ) {
    if( !mInputManager ) {
        mInputManager = new DIVA_InputManager();
    }

    return mInputManager;
}


void DIVA_InputManager::eventContinue( void ) {
	itKeyEvent = keyEventCont.begin();
	itKeyEventEnd = keyEventCont.end();
	for (; itKeyEvent != itKeyEventEnd; itKeyEvent++){
		itKeyEvent->first->keyPressed(itKeyEvent->second);
	}
}
void DIVA_InputManager::disableKeyListener( const std::string& instanceName ) { 	enKeyListeners[ instanceName ] = false ;}
void DIVA_InputManager::disableMouseListener( const std::string& instanceName ) { 	enMouseListeners[ instanceName ] = false ; }
void DIVA_InputManager::disableJoyStickListener( const std::string& instanceName ) {	enJoystickListeners[ instanceName ] = false ; }
void DIVA_InputManager::enableKeyListener( const std::string& instanceName ) {   enKeyListeners[ instanceName ] = true ; }
void DIVA_InputManager::enableMouseListener( const std::string& instanceName ) { enMouseListeners[ instanceName ] = true ; }
void DIVA_InputManager::enableJoyStickListener( const std::string& instanceName ) {  enJoystickListeners[ instanceName ] = true ; }
